/**
 * 对某个文件大小为1.16GB的一次测试结果
 * 
 * 加密类型 keySize 加密所用时间 解密使用时间 AES 128 11秒 10秒 192 15秒 11秒 256 18秒 12秒 DES 56 30秒 29秒 3DES 112 80秒
 * 79秒 168 81秒
 */
package cn.nawang.ebeim.client.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

/**
 * @ClassName: CipherUtil
 * @Description: 使用AES对文件进行加密和解密
 * @author 张建峰
 * @date 2015年8月12日09:54:22
 * @version v1.0
 */
public class CipherUtil {

//  public static void main(String[] args) {
//
//    long startTime = System.currentTimeMillis();
//
//     new CipherUtil(CipherUtil.AES,CipherUtil.AES_KEY_SIZE_128).encrypt(
//     "F:\\1.0\\dddddd\\zjf1 (2).jpg",
//     "F:\\1.0\\dddddd\\zjf1 (2).xxx",
//     "password");
//     
//     System.out.println(new File("F:\\1.0\\dddddd\\zjf1 (2).xxx").length());
//     System.out.println(new File("F:\\1.0\\dddddd\\zjf1 (2).jpg").length());
//     
////     new CipherUtil(CipherUtil.AES, CipherUtil.AES_KEY_SIZE_128).decrypt(
////       "E:\\test\\encryptDescrypt\\container\\myeclipse.ebeim",
////       "E:\\test\\encryptDescrypt\\container\\myeclipse.exe", "zhangjianfeng");
//
//    long endTime = System.currentTimeMillis();
//
//    System.out.println("\n完成时间：");
//    System.out.println((endTime - startTime) + "毫秒.");
//    System.out.println(((endTime - startTime) / 1000) + "秒.");
//
//  }

  public static final String AES = "AES";
  public static final String DES = "DES";
  public static final String THRIPLE_DES = "DESede";

  public static final int AES_KEY_SIZE_128 = 128;
  public static final int AES_KEY_SIZE_192 = 192;
  public static final int AES_KEY_SIZE_256 = 256;

  public static final int THRIPLE_DES_KEY_SIZE_112 = 112;
  public static final int THRIPLE_DES_KEY_SIZE_168 = 168;

  public static final int DES_KEY_SIZE_56 = 56;

  // 默认使用AES对文件进行加密和解密
  private String type = CipherUtil.AES;
  // 默认的keySize=128
  private int keySize = CipherUtil.AES_KEY_SIZE_128;

  public CipherUtil(String type, int keySize) {
    this.keySize = keySize;
    this.type = type;
  }

  /**
   * 默认使用AES
   * 
   * @param keySize
   */
  public CipherUtil(int keySize) {
    this.keySize = keySize;
  }

  /**
   * 默认使用AES 默认keySize 128
   */
  public CipherUtil() {}

  /**
   * @author 张建峰
   * @Description: 把文件srcFile加密后存储为destFile
   * @param @param srcFile 加密前的文件
   * @param @param destFile 加密后的文件
   * @param @param privateKey 密钥
   * @return void
   * @date 2015年8月12日上午9:54:35
   *
   * @修改人 张建峰
   * @修改日期
   */
  public void encrypt(String srcFile, String destFile, String privateKey) {

    FileInputStream fis = null;
    FileOutputStream fos = null;

    try {
      Key key = getKey(privateKey);
      Cipher cipher = Cipher.getInstance(type + "/ECB/PKCS5Padding");
      cipher.init(Cipher.ENCRYPT_MODE, key);

      fis = new FileInputStream(srcFile);
      File file = mkdirFiles(destFile);
      
      int tmp = 0;
      while(!file.exists()&&tmp<10){
        Thread.sleep(100);
        tmp++;
      }
      
      fos = new FileOutputStream(file);

      crypt(fis, fos, cipher);
      System.out.println("加密结束");
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (fis != null) {
        try {
          fis.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  /**
   * @author 张建峰
   * @Description: 把文件srcFile解密后存储为destFile
   * @param @param srcFile 解密前的文件
   * @param @param destFile 解密后的文件
   * @param @param privateKey 密钥
   * @return void
   * @date 2015年8月12日上午9:55:25
   *
   * @修改人 张建峰
   * @修改日期
   */
  public void decrypt(String srcFile, String destFile, String privateKey) {
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
      Key key = getKey(privateKey);
      Cipher cipher = Cipher.getInstance(type + "/ECB/PKCS5Padding");
      cipher.init(Cipher.DECRYPT_MODE, key);

      fis = new FileInputStream(srcFile);
      fos = new FileOutputStream(mkdirFiles(destFile));

      crypt(fis, fos, cipher);
      System.out.println("解密结束");
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (fis != null) {
        try {
          fis.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  /**
   * @author 张建峰
   * @Description: 根据filePath创建相应的目录
   * @param @param filePath 要创建的文件路经
   * @param @return
   * @param @throws IOException
   * @return File 文件
   * @date 2015年8月12日上午9:55:55
   *
   * @修改人 张建峰
   * @修改日期
   */
  private File mkdirFiles(String filePath) throws IOException {
    File file = new File(filePath);
    if (!file.getParentFile().exists()) {
      file.getParentFile().mkdirs();
    }
    file.createNewFile();

    return file;
  }

  /**
   * @author 张建峰
   * @Description: 生成指定字符串的密钥
   * @param @param secret 要生成密钥的字符串
   * @param @return
   * @return Key 生成后的密钥
   * @date 2015年8月12日上午9:56:26
   *
   * @修改人 张建峰
   * @修改日期
   */
  private Key getKey(String secret) {
    KeyGenerator kgen = null;
    try {
      kgen = KeyGenerator.getInstance(type);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    kgen.init(keySize, new SecureRandom(secret.getBytes()));
    SecretKey secretKey = kgen.generateKey();
    return secretKey;
  }

  /**
   * @author 张建峰
   * @Description: 加密解密流
   * @param @param in 加密解密前的流
   * @param @param out 加密解密后的流
   * @param @param cipher 加密解密
   * @param @throws IOException
   * @param @throws GeneralSecurityException
   * @return void
   * @date 2015年8月12日上午9:57:05
   *
   * @修改人 张建峰
   * @修改日期
   */
  private void crypt(InputStream in, OutputStream out, Cipher cipher)
      throws IOException, GeneralSecurityException {
    int blockSize = cipher.getBlockSize() * 1000;
    int outputSize = cipher.getOutputSize(blockSize);

    byte[] inBytes = new byte[blockSize];
    byte[] outBytes = new byte[outputSize];
    int inLength = 0;
    boolean more = true;
    // 文件总长度
    long size = ((FileInputStream) in).getChannel().size();
    // 文件已读长度
    double sum = 0;

    while (more) {
      inLength = in.read(inBytes);
      if (inLength == blockSize) {
        int outLength = cipher.update(inBytes, 0, blockSize, outBytes);
        out.write(outBytes, 0, outLength);

        // ---------控制台打印已完成的百分率-------------
        sum += outBytes.length;
        double percent = (sum * 100 / size);
        percent = new BigDecimal(percent).setScale(1, BigDecimal.ROUND_HALF_DOWN).doubleValue();
        percent = percent > 100 ? 100 : percent;
     /*   System.out.println("已完成:" + percent + "%");*/
        // ---------控制台打印已完成的百分率-------------

      } else {
        more = false;
      }
    }
    if (inLength > 0)
      outBytes = cipher.doFinal(inBytes, 0, inLength);
    else
      outBytes = cipher.doFinal();
    out.write(outBytes);
  }
}
